package com.thz.houserental.infrastructure.advice.log;

import com.alibaba.fastjson2.JSON;
import com.thz.houserental.infrastructure.log.BusinessStatus;
import com.thz.houserental.infrastructure.table.LogRecord;
import com.thz.houserental.infrastructure.util.IPUtils;
import com.thz.houserental.infrastructure.util.RequestUtils;
import com.thz.houserental.infrastructure.web.service.LogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

@Aspect
@Component
@Slf4j
public class LogAspect {
    private final LogService logService;
    public LogAspect(LogService logService) {
        this.logService = logService;
    }

    @Pointcut("@annotation(Log)")
    public void logPointCut() {
    }

    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleLog(joinPoint, null, jsonResult);
    }

    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    private void handleLog(JoinPoint joinPoint, Exception e, Object jsonResult) {
        try {
            Log controllerLog = this.getAnnotationLog(joinPoint);
            if (controllerLog == null) return;

            LogRecord logDo = LogRecord.builder()
                    .status(BusinessStatus.SUCCESS.ordinal())
                    .requestUrl(RequestUtils.getURL())
                    .requestIp(IPUtils.getIpAddress(RequestUtils.getSimpleMethod()))
                    .jsonResult(JSON.toJSONString(jsonResult))
                    .method(this.getRequestMethodName(joinPoint))
                    .requestMethod(RequestUtils.getMethod())
                    .businessType(String.valueOf(controllerLog.businessType().ordinal()))
                    .title(controllerLog.title())
                    .operator("root")
                    .build();

            //相关异常
            if (e != null) {
                logDo.setStatus(BusinessStatus.FAIL.ordinal());
                logDo.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }

            // 是否需要保存request，参数和值
            if (controllerLog.isSaveRequestData()) {
                setRequestValue(joinPoint, logDo);
            }

            logService.save(logDo);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    private String getRequestMethodName(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        return className + "." + methodName + "()";
    }

    private Log getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(Log.class);
        }
        return null;
    }

    private void setRequestValue(JoinPoint joinPoint, LogRecord operLog) {
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setRequestParam(StringUtils.substring(params, 0, 2000));
        } else {
            Map<?, ?> paramsMap = (Map<?, ?>) ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            operLog.setRequestParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }
    }

    private String argsArrayToString(Object[] paramsArray) {
        StringBuilder params = new StringBuilder();
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (!isFilterObject(o)) {
                    Object jsonObj = JSON.toJSON(o);
                    params.append(jsonObj.toString()).append(" ");
                }
            }
        }
        return params.toString().trim();
    }

    public boolean isFilterObject(final Object o) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}

